﻿using System;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using Oxage.Common;
using Oxage.Services;
using Oxage.Simulator;
using Oxage.Controls;

namespace Oxage.Models
{
	public partial class SimulatorViewModel : ViewModelBase
	{
		#region Constructor
		public SimulatorViewModel()
		{
			InitializeStepperSimulator();
		}
		#endregion

		#region Public properties
		public string Content
		{
			get
			{
				return this.content;
			}
			set
			{
				if (this.content != value)
				{
					this.content = value;
					OnPropertyChanged("Content");
				}
			}
		}
		private string content;

		public Path Preview
		{
			get
			{
				return this.preview;
			}
			set
			{
				if (this.preview != value)
				{
					this.preview = value;
					OnPropertyChanged("Preview");
				}
			}
		}
		private Path preview;

		public IStepDriver Driver
		{
			get
			{
				return this.driver;
			}
			set
			{
				if (this.driver != value)
				{
					this.driver = value;
					OnPropertyChanged("Driver");
				}
			}
		}
		private IStepDriver driver;
		#endregion

		#region Public methods
		public void QuickDraw(string gcode)
		{
			Clear();

			var helper = new StepMakerHelper(this.Driver);
			helper.RunGCode(App.ViewModel.Document.Content);
		}

		public Path Draw(string code)
		{
			var figure = new PathFigure();
			figure.StartPoint = new Point(0, 0);

			var geometry = new PathGeometry();
			geometry.Figures.Add(figure);

			string[] lines = code.Split('\n', '\r');
			foreach (string line in lines)
			{
				try
				{
					double g = (double)GetValue<double>(line, 'G');
					double x = (double)GetValue<double>(line, 'X');
					double y = (double)GetValue<double>(line, 'Y');
					double z = (double)GetValue<double>(line, 'Z');

					//G01 - line
					figure.Segments.Add(new LineSegment() { Point = new Point(x, y) });
				}
				catch
				{
				}
			}

			Path path = new Path();
			path.Data = geometry;
			path.StrokeThickness = 5;
			path.Stroke = new SolidColorBrush(Colors.White);
			path.Stretch = Stretch.Uniform;
			this.Preview = path;
			return path;
		}

		public void Clear()
		{
			this.Preview = new Path();
			(this.Driver as VectorSimulationCanvas).Clear();
		}
		#endregion

		#region Protected methods
		protected void Parse()
		{
			string line = "N001 X100 Y200 Z300 G01";
			double x = (double)GetValue<double>(line, 'X');
			double y = (double)GetValue<double>(line, 'Y');
			double z = (double)GetValue<double>(line, 'Z');
		}

		protected string GetValue(string line, char code)
		{
			string result = null;
			string pattern = "" + code + @"([\d\s\.,]+)";
			var match = Regex.Match(line, pattern, RegexOptions.IgnoreCase);
			if (match.Success)
			{
				result = match.Groups[1].Value.Trim();
			}
			return result;
		}

		protected object GetValue<T>(string line, char code) where T : IConvertible
		{
			object result = default(T);
			string value = GetValue(line, code);
			if (typeof(T) == typeof(int))
			{
				result = int.Parse(value);
			}
			else if (typeof(T) == typeof(double))
			{
				result = double.Parse(value);
			}
			return result;
		}
		#endregion

		#region Stepper driver simulator
		public const int AXIS_X = 0;
		public const int AXIS_Y = 1;
		public const int AXIS_Z = 2;

		private bool[] enable = new bool[] { false, false, false };
		private bool[] dir = new bool[] { false, false, false };

		private int x = 0;
		private int y = 0;
		private int z = 0;

		public void Enable(int axis, bool enable)
		{
			this.enable[axis] = enable;
		}

		public void Dir(int axis, bool dir)
		{
			this.dir[axis] = dir;
		}

		public void Step(int axis)
		{
			if (this.enable[axis])
			{
				bool dir = this.dir[axis];
				switch (axis)
				{
					case AXIS_X:
						x = (dir ? x + 1 : x - 1);
						break;

					case AXIS_Y:
						y = (dir ? y + 1 : y - 1);
						break;

					case AXIS_Z:
						z = (dir ? z + 1 : z - 1);
						break;
				}
				figure.Segments.Add(new LineSegment() { Point = new Point(x, y), IsSmoothJoin = true, IsStroked = true });
				LoggingService.WriteLine(string.Format("Absolute coordinates: X = {0}, Y = {1}, Z = {2}", x, y, z));
			}
			else
			{
				LoggingService.WriteLine("Enable axis before making a step.");
			}
		}

		public void SimulateBlock(byte[] buffer)
		{
			if (buffer == null || buffer.Length != 5)
			{
				throw new Exception("Expected 5 bytes");
			}

			SimulateMask(AXIS_X, buffer[1]);
			SimulateMask(AXIS_Y, buffer[2]);
			SimulateMask(AXIS_Z, buffer[3]);
		}

		public void SimulateMask(int axis, byte mask)
		{
			//True if bit 0 is set
			Enable(axis, (mask & (1 << 0)) > 0);

			//True if bit 1 is set
			Dir(axis, (mask & (1 << 1)) > 0);

			//True if bit 2 is set
			if ((mask & (1 << 2)) > 0)
			{
				Step(axis);
			}
		}

		private PathFigure figure = null;

		protected void InitializeStepperSimulator()
		{
			this.figure = new PathFigure();
			this.figure.StartPoint = new Point(0, 0);

			var geometry = new PathGeometry();
			geometry.Figures.Add(this.figure);

			var p = new Path();
			p.Data = geometry;
			p.StrokeThickness = 2;
			p.Stroke = new SolidColorBrush(Colors.White);
			p.Stretch = Stretch.None;
			this.Preview = p;

			//Test
			//SimulateBlock(new byte[] { 0x02, 0x07, 0x05, 0x00, 0x03 });
		}
		#endregion
	}
}
